/* CQFS Engine
 * Copyright (c) 2006 Blazing Games Inc. All Rights Reserved
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; 
 * version 2.1 of the License.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package com.blazinggames.cqfs.data;

import java.util.Vector;

/**
 * The heart of resource management begins with having some knowledge about 
 * resources. This class holds the information necessary to track the use of 
 * a resource. If the resource has already been loaded, the class holds a 
 * reference to that resource in memory. If the resource has not been loaded 
 * yet, the information on how to obtain that resource can be used to load 
 * the resource. For the proper management of a resource, each resource is 
 * assigned a unique number by the resource manager and a unique name by 
 * the user. Besides the loading/locking state of a resource, the information 
 * tracks the priority of a resource, the weight of a resource, and the last 
 * time a resource was requested.
 * 
 * @author Billy D. Spelchan
 * @version 0.1
 * Created on 26-Sep-06
 */
public class ResourceInfo
{
// *********************
// ***** CONSTANTS *****
// *********************
	
	public static final int STATE_NOT_LOADED = 0;
	public static final int STATE_ERROR = 1;
	public static final int STATE_LOADED = 2;
	public static final int STATE_LOCKED = 3;

	public static final int DEFAULT_PRIORITY = 5;
	public static final int DEFAULT_WEIGHT = 1;
	
// *********************
// ***** VARIABLES *****
// *********************

	private Vector _stateListeners; 
	private Factory _factory;
	private String _parameters;
	private String _gun;
	private long _guid;
	private long _lastAccess;
	private Object _actualResource;
	private int _state;
	private int _priority;
	private int _weight;
	
	
// ***********************
// ***** CONSTRUCTOR *****
// ***********************
	
	/**
	 * Constructor.
	 * 
	 * @param rsl The resource manager that created the class
	 * @param factory The factory to use to create the object
	 * @param paramter The parameters to give to the factory
	 * @param gun The Globally Unique Name the user assigned to the resource 
	 * @param guid The Globally Unique ID the resource manager assigned
	 */
	public ResourceInfo(ResourceStateListener rsl, Factory factory, String parameters, String gun, long guid)
	{
		_stateListeners = new Vector();
		_stateListeners.add(rsl);
		_factory = factory;
		_parameters = parameters;
		_gun = gun;
		_guid = guid;
		_lastAccess = 0;
		_actualResource = null;
		_state = STATE_NOT_LOADED;
		_priority = DEFAULT_PRIORITY;
		_weight = DEFAULT_WEIGHT;
	}

// *******************
// ***** METHODS *****
// *******************
	
	/**
	 * Adds a class to the list of state listeners.
	 * 
	 * @param rsl the class that wants to be informed of resource state events
	 */
	public void addStateListener(ResourceStateListener rsl)
	{
		_stateListeners.add(rsl);
	}
	
	/**
	 * Removes a class from the list of resource state listeners
	 * 
	 * @param rsl the class that wants to be removed
	 */
	public void removeStateListener(ResourceStateListener rsl)
	{
		_stateListeners.remove(rsl);
	}
	
	/**
	 * Internal method to let all listeners know about a state change.
	 * 
	 * @param type Type of state change to report
	 * @see com.blazinggames.cqfs.ResourceInfo
	 */
	protected void sendChangeAlert(int type)
	{
		ResourceStateListener rsl;
		for (int cntr = 0; cntr < _stateListeners.size(); ++cntr) {
			rsl = (ResourceStateListener)_stateListeners.get(cntr);
			rsl.resourceStateChanged(this, type);
		}
	}
	
	/**
	 * Returns the Globally Unique Name assigned to this resource
	 * 
	 * @return the Globally Unique Name assigned to this resource
	 */
	public String getGUN()
	{
		return _gun;
	}
	
	/**
	 * Returns the Globally Unique ID the resource manager assigned to this resource
	 * 
	 * @return the Globally Unique ID the resource manager assigned to this resource
	 */
	public long getGUID()
	{
		return _guid;
	}
	
	/**
	 * Returns the system time that the last access to this object occurred.
	 * 
	 * @return the system time that the last access to this object occurred.
	 */
	public long getLastAccess()
	{
		return _lastAccess;
	}
	
	/**
	 * Gets the actual resource. If the resource has not been assigned yet, 
	 * it will be loaded (or whatever the factory does to create the resource).
	 * 
	 * @return the resource object
	 */
	public Object getResourceObject()
	{
		if (_actualResource == null) {
			_actualResource = _factory.create(_parameters);
			_state = STATE_LOADED;
			sendChangeAlert(ResourceStateListener.TYPE_STATE);
		}
		_lastAccess = System.currentTimeMillis();
		return _actualResource;
	}

	/**
	 * This should only be called by the resource manager! It removes the 
	 * resource from memory.
	 * 
	 * @throws Exception if the resource is locked!
	 */
	public void removeResource()
	throws Exception 
	{
		if (_state == STATE_LOCKED)
			throw new Exception("Resource Locked Exception");
		_state = STATE_NOT_LOADED;
		if (_actualResource != null) {
			_factory.recycle(_actualResource);
			_actualResource = null;
		}
		sendChangeAlert(ResourceStateListener.TYPE_STATE);
	}
	
	/**
	 * locks the resource. If the resource is not loaded, will load it first! 
	*/
	public void lockResource()
	{
		getResourceObject(); // make sure loaded
		_state = STATE_LOCKED;
		sendChangeAlert(ResourceStateListener.TYPE_STATE);
	}
	
	/**
	 * Unlocks the resource (must be done before a resource can be removed)
	 *
	 */
	public void unlockResource()
	{
		if (_state == STATE_LOCKED) {
			_state = STATE_LOADED;
			sendChangeAlert(ResourceStateListener.TYPE_STATE);
		}
	}
	
	/**
	 * Changes the priority of the resource. The priority is used by the 
	 * resource manager to decide which resources should be removed. Vital
	 * resources should be locked so they can't be removed, but important
	 * resources can be given a high priority which will reduce the likelyhood
	 * that the resource gets removed (though how that is determined is
	 * up to the actual resource manager)
	 * 
	 * @param priority How important the resource is
	 * @return previous priority
	 */
	public int changePriority(int priority)
	{
		int oldPriority = _priority;
		
		if (_priority != priority)
		{
			_priority = priority;
			sendChangeAlert(ResourceStateListener.TYPE_PRIORITY);
		}
		
		return oldPriority;
	}
	
	/**
	 * gets the current priorty level assigned to the resource.
	 * 
	 * @return the current priorty level assigned to the resource
	 */
	public int getPriority()
	{
		return _priority;
	}
	
	/**
	 * Because Java doesn't give you the memory footprint of classes, the
	 * weight is a value to help the resource manager determine how many
	 * resources a resource takes up. The resource manager may use weights
	 * to determine how many objects to allow in memory in which case the
	 * weights determine how many resources are loaded (of course, the 
	 * resource manager may just use memory information and if there is 
	 * too little memory will discard resources but that is up to the
	 * memory manager to decide).
	 * 
	 * @param weight How big of resource footprint this object makes
	 * 
	 * @return previous weight
	 */
	public int changeWeight(int weight)
	{
		int oldWeight = _weight;
		
		if (_weight != weight) {
			_weight = weight;
			sendChangeAlert(ResourceStateListener.TYPE_WEIGHT);
		}
		
		return oldWeight;
	}

	/**
	 * Returns the resource footprint (weight) of this resource
	 * 
	 * @return the resource footprint (weight) of this resource
	 */
	public int getWeight()
	{
		if (_state == STATE_NOT_LOADED)
			return 0;
		else
			return _weight;
	}
}
